/* eslint-disable no-nested-ternary */
const fs = require('fs-extra');
const path = require('path');
const mongoose = require('mongoose');
const randomString = require('randomstring');
const ImageModule = require('docxtemplater-image-module-free');

const utility = require('../../../utility');
const Docx = require('./../adapter/docx');
const ParentReporter = require('./../default-report');
const ProjectVersionSpliter = require('./../../project-version-spliter');
const files = require('./../../../files');
const functionsModel = require('./../../../functions');
const tests = require('./../../../tests');
const fileElements = require('./../../../file-elements');
const logger = require('./../../../logger');
const { mcdcKind, testType } = require('./../../../tests');
const classCreator = require('./transformer');
const {
  generateStatemetBlocks,
  generateBranchBlocks,
  generateConditionBlocks,
  generateMcDcBlocks,
  generateCondMcDcBlocks,
  generateDecisionBlocks,
} = require('./../functionUtil');
const { versionType } = require('../../../project-versions');
const { resultKind } = require('../../../tests');

const typeNameDic = {
  [testType.gn]: '功能',
  [testType.jk]: '接口',
  [testType.bj]: '边界',
  [testType.lj]: '逻辑',
  [testType.xn]: '性能',
};

function arrayToMap(arr, key) {
  const map = new Map();
  arr.forEach((item) => {
    map.set(item[key].toString(), item);
  });
  return map;
}

class DefaultReporter extends ParentReporter {
  constructor(versionId, locale, version = null, project = null, creator = null, outputDir = null, format, complete) {
    super(versionId, locale, version, project, creator, outputDir, format, complete);
    this.baseDir = __dirname;
    this.images = new Map();
    this.summaryTestsSet = []; // 测试用例集
  }

  async handleFile(file, versionDir) {
    const reportPath = `${path.join(versionDir, path.dirname(file.path), file.fileName)}`;
    const res = await ProjectVersionSpliter.prototype.handleFile.call(this, file, versionDir);
    const { typeSystem, functions: funcs, resultCount } = res;
    if (!resultCount) {
      return Promise.resolve();
    }
    const fileContent = await files.getLinesOfRemoteFileStream(file._id, {});
    const linesInSourceFile = fileContent.map(({ content }) => content);
    await utility.arrayChunkOperation(funcs, 10, async (func) => {
      func.functionCfg = await this.getDataAdapter().getFunctionCfg(func);
    });
    if (this.willTestStatements()) {
      generateStatemetBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestBranch()) {
      generateBranchBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestCondition()) {
      generateConditionBlocks(funcs);
    }
    if (this.willTestDecision()) {
      generateDecisionBlocks(funcs, linesInSourceFile);
    }
    if (this.willTestMcdc()) {
      // 算法一
      if (this.version.mcdcKind === mcdcKind.masking) {
        generateMcDcBlocks(funcs);
      // 算法二
      } else {
        generateCondMcDcBlocks(funcs);
      }
    }
    funcs.map((func) => {
      func.codeCoverageDetails = [];
      if (this.willTestStatements()) {
        func.codeCoverageDetails.push(func.statementsBlocks);
      }
      if (this.willTestBranch() && func.branchTotal > 0) {
        func.codeCoverageDetails.push(func.branchBlocks);
      }
      if (this.willTestCondition() && func.conditionTotal > 0) {
        func.codeCoverageDetails.push(func.conditionsBlocks);
      }
      if (this.willTestDecision() && func.decisionTotal > 0) {
        func.codeCoverageDetails.push(func.decisionBlocks);
      }
      if (this.willTestMcdc() && func.mcdcTotal > 0) {
        func.codeCoverageDetails.push(func.mcdcBlocks || func.condMcdcBlocks);
      }
      return func;
    });
    await fs.ensureDir(path.dirname(reportPath));
    const data = {
      projectName: this.project.projectName,
      fileName: file.fileName,
      summary: DefaultReporter.getSummaryOfFile(funcs),
      functions: this.getFunctionDetailsOfFile(typeSystem, funcs),
      isIntegrationTest: this.isIntegrationTest(),
      reportName: this.getReportName(),
      headerRightContent: `${this.getReportName()} - ${file.fileName}`,
      reportFooterText: this.i18n.__('tests.copyright', { year: (new Date()).getFullYear() }),
    };
    data.functions.forEach((func) => {
      func.tests.forEach((test) => {
        test.index = test.index.toString().padStart(3, '0');
        test.typeName = `${typeNameDic[test.typeNumber.substr(0, 2)] || '功能'}测试`;
        test.result = test.resultKind === 'unknown' ? 0 : (['succeed', 'nonStrictSucceed', 'noResult'].indexOf(test.resultKind) >= 0 ? 1 : 2);
        const newInputDetails = [];
        const newOutputDetails = [];
        let currentCategory = '';
        const description = [];
        let tmpVariable = [];
        if (utility.isArrayType(test.detail.input)) {
          test.detail.input.forEach((input) => {
            // eslint-disable-next-line no-control-regex
            input.variable = input.variable.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
            if (input.category && input.category !== currentCategory) {
              currentCategory = input.category;
            }
            if (currentCategory === '形参' || currentCategory === '全局变量') {
              const variable = `${input.kind === 23 ? input.name : input.originalName || input.name}`;
              if (variable) {
                tmpVariable.push(variable);
              }
            }
            if (input.value !== '') {
              input.value = ` = ${input.value}`;
              if (currentCategory === '形参' || currentCategory === '全局变量') {
                description.push(`${input.typeName} ${tmpVariable.join('.')}${input.value}`);
                tmpVariable = [];
              }
            }
            if (input.category && input.variable) {
              newInputDetails.push({
                category: `${input.category}:`,
                value: '',
                variable: '',
              });
              newInputDetails.push({
                category: '',
                value: input.value,
                variable: input.variable,
              });
            } else {
              newInputDetails.push(input);
            }
          });
        }
        if (utility.isArrayType(test.detail.output)) {
          test.detail.output.forEach((output) => {
            // eslint-disable-next-line no-control-regex
            output.variable = output.variable.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
            const orgValue = output.value;
            output.value = output.value !== ''
              ? ` = ${output.value}`
              : (output.result !== '' ? ` = ${output.result}` : '');
            // 处理变量值显示格式
            if (output.result !== '') {
              output.result = ` = ${output.result}`;
            }
            if (output.category && output.variable) {
              newOutputDetails.push({
                category: `${output.category}:`,
                orgValue: '',
                value: '',
                result: '',
                variable: '',
              });
              newOutputDetails.push({
                category: '',
                orgValue,
                value: output.value,
                result: output.result,
                variable: output.variable,
              });
            } else {
              newOutputDetails.push(output);
            }
          });
        }
        test.detail.input = newInputDetails;
        test.detail.output = newOutputDetails;
        test.detail.testcaseOutput = [];
        test.description = `针对${func.functionName}函数的测试${description.length > 0 ? `(${description.join(',')})` : ''}`;
        let varClear = true;
        let categoryClear = true;
        for (let i = test.detail.output.length - 1; i >= 0; i -= 1) {
          if (newOutputDetails[i].orgValue !== '') {
            varClear = false;
            categoryClear = false;
          }
          if ((newOutputDetails[i].category !== '' && !categoryClear)
            || (newOutputDetails[i].category === '' && !varClear)) {
            test.detail.testcaseOutput.push(newOutputDetails[i]);
          }
          if (i > 0 && newOutputDetails[i - 1].value !== '') {
            varClear = true;
          }
          if (newOutputDetails[i].category !== '') {
            categoryClear = true;
          }
        }
        test.detail.testcaseOutput = test.detail.testcaseOutput.reverse();
      });
      this.summaryTestsSet.push(...func.tests);
    });
    if (this.isIntegrationTest()) {
      const nextLevels = await Promise.all(
        data.functions.map((func) => functionsModel.getInvokeChildren(func.id, func.id, this.version._id, 4, false))
      );
      data.functions.forEach((func, index) => {
        func.nextLevel = nextLevels[index];
      });
      await utility.arrayChunkOperation(data.functions, 1, async (func) => {
        await this.generateFuncInvokeFuncImage(func);
        delete func.nextLevel;
      });
    }
    let docx = new Docx(path.join(__dirname, 'template', 'docx', 'file-template.docx'));
    docx.getAdapterInstance().attachModule(new ImageModule({
      centered: false,
      getImage: (reportPath) => fs.readFileSync(reportPath),
      getSize: (img, tagValue) => {
        if (!this.images.has(tagValue)) {
          return [0, 0];
        }
        const imageConfig = this.images.get(tagValue);
        if (!imageConfig) {
          throw new Error(`Can not get image size of ${imageConfig}`);
        }
        return [imageConfig.width, imageConfig.height];
      },
    }));
    await docx.generate(data, `${reportPath}.docx`);
    docx = new Docx(path.join(__dirname, 'template', 'docx', 'file-template-testcases.docx'));
    docx.getAdapterInstance().attachModule(new ImageModule({
      centered: false,
      getImage: (reportPath) => fs.readFileSync(reportPath),
      getSize: (img, tagValue) => {
        if (!this.images.has(tagValue)) {
          return [0, 0];
        }
        const imageConfig = this.images.get(tagValue);
        if (!imageConfig) {
          throw new Error(`Can not get image size of ${imageConfig}`);
        }
        return [imageConfig.width, imageConfig.height];
      },
    }));
    await docx.generate(data, `${path.join(versionDir, path.dirname(file.path), file.fileName)}测试用例.docx`);
    return { reportPath };
  }

  async generateFuncInvokeFuncImage(rootFunc) {
    const funcId = rootFunc.id.toString();
    const gray = 'rgb(164, 177, 191)';
    const green = 'rgb(27, 173, 39)';
    const blue = 'rgb(59, 109, 242)';
    const fontblack = 'rgb(102, 102, 102)';
    const fontwhite = 'white';
    const graybg = 'rgb(219, 219, 219)';
    const whitebg = 'white';

    let leafCount = 0; // 记录叶子节点数量，用于估算图片尺寸height
    let depth = 1;
    const tree = {
      functionId: funcId,
      functionName: rootFunc.functionName,
      children: rootFunc.nextLevel,
      // echarts数据 根节点
      label: {
        position: 'left',
        align: 'left',
        distance: 0,
        backgroundColor: blue,
        color: fontwhite,
      },
      name: DefaultReporter.truncateText(rootFunc.functionName),
    };
    function resc(func) {
      if (func.children && func.children.length > 0) {
        depth += 1;
        func.children = func.children.map((child) => resc(child));
      } else {
        leafCount += 1;
      }
      const node = {};
      node.covered = false;
      node.itemStyle = {
        borderWidth: 0,
        color: node.covered ? green : gray,
      };
      node.lineStyle = {
        borderWidth: 0,
        color: node.covered ? green : gray,
      };
      // 函数指针
      if (func.functionId) {
        node.name = `⊱ ${func.functionName}`;
      } else if (func.functionType === 'funcPointer') {
        node.name = `> ${func.functionName}`;
      } else {
        node.name = `? ${func.functionName}`;
      }
      node.name = DefaultReporter.truncateText(node.name);
      node.label = {
        backgroundColor: node.covered ? green : (func.enableStub ? graybg : whitebg),
        color: node.covered ? fontwhite : fontblack,
        borderColor: 'rgb(164, 177, 190)',
        borderWidth: node.covered ? 0 : (func.enableStub ? 0 : 1),
        align: 'left',
      };
      if (func.children && func.children.length > 0) {
        node.children = func.children;
      }
      return node;
    }
    resc(tree);
    const options = {
      series: [
        {
          type: 'tree',
          data: [tree],
          initialTreeDepth: 10,
          top: '1%',
          left: '1%',
          bottom: '1%',
          right: depth > 4 ? '15%' : (depth > 3 ? '35%' : '50%'),
          symbolSize: 20,
          symbol: 'triangle',
          symbolRotate: -90,
          orient: 'LR',
          label: {
            fontSize: 40,
            position: 'right',
            distance: 0,
            verticalAlign: 'middle',
            align: 'left',
            backgroundColor: green,
            borderRadius: 5,
            padding: 20,
          },
        },
      ],
    };

    // 估算图片尺寸
    const minHeight = 200;
    const height = leafCount * 25 > minHeight ? leafCount * 25 : minHeight;
    const width = 600;

    let outputPath = path.join(this.outputDir, `${randomString.generate(16)}.png`);
    try {
      await utility.writeFile(DefaultReporter.getEChartsImage(options, { width, height }), outputPath);
      this.images.set(outputPath, {
        width, height,
      });
    } catch (err) {
      logger.error(`functionName:${tree.functionName}, leafCount:${leafCount}`, err);
      outputPath = '';
    }
    rootFunc.invokeRelationsImage = outputPath;
  }

  getFunctionDetailsOfFile(typeSystem, functions) {
    const resFunctions = [];
    functions.sort((a, b) => {
      if (a.index < b.index) return -1;
      if (a.index > b.index) return 1;
      return 0;
    });
    functions.forEach((func) => {
      if (!func.tests.length) {
        return;
      }
      resFunctions.push({
        id: func._id.toString(),
        functionName: func.functionName,
        description: func.description,
        udfs: func.udfs,
        codeDefects: this.getCodeDefects(func),
        // to be modified
        summary: this.getSummaryOfFunction(func),
        tests: this.getTestDetailsOfFunction(typeSystem, func),
        integrationFunctions: func.integrationFunctions,
        integrationFunctionsCount: func.integrationFunctions.length,
      });
    });
    return resFunctions;
  }

  static truncateText(str, maxLength = 20) {
    if (str.length > maxLength) {
      return `${str.slice(0, maxLength - 3)}...`;
    }
    return str;
  }

  static versionTypeSupport(vType) {
    return vType === versionType.unitTest || vType === versionType.integrationTest;
  }

  async handleSummary(summary) {
    await super.handleSummary(summary);
    summary.functionsOverview = summary.functionsOverview.filter((f) => {
      if (f.testCount === 0) {
        return false;
      }
      return true;
    });
    const allFiles = await this.getDataAdapter().getFiles(this.versionId);
    const testsResultByTypename = [
      'GN', // 功能
      'JK', // 借口
      'BJ', // 边界
      'LJ', // 逻辑
      'XN', // 性能
      'ALL', // 合计
    ].reduce((o, typeName) => {
      o[typeName] = {
        total: 0,
        excuted: 0,
        unexcuted: 0,
        passed: 0,
        notPassed: 0,
      };
      return o;
    }, {});
    const { enabledCoverageKind } = summary;
    enabledCoverageKind.forEach((item) => {
      item.shortname = item.name.slice(0, item.name.length - 2);
    });
    summary.testsResultByTypename = testsResultByTypename;
    summary.allFiles = allFiles;
    const allFuncs = [];
    const allTests = [];
    const classMap = {};
    await utility.arrayChunkOperation(allFiles, 1, async (f) => {
      let funcs = await this.getDataAdapter().getFunctions(this.versionId, f._id);
      // mongoose类型在docxtemplater中读取字段为undefined
      funcs = funcs.filter((f) => {
        if (f.kind === fileElements.elementKind.class) {
          classMap[f._id.toString()] = f.functionName;
        }
        return fileElements.isTestableElement(f.kind);
      }).map((f) => {
        if (f.toObject) {
          return f.toObject();
        }
        return f;
      });
      f.funcs = funcs;
      funcs.forEach((func) => {
        func.fileName = f.fileName;
      });
      allFuncs.push(...funcs);
      const tests = await this.getDataAdapter().getFileTests(f._id);
      // console.log(tests);
      f.tests = tests;
      allTests.push(...tests);
      tests.forEach((t) => {
        const typename = t.typeName || 'GN';
        testsResultByTypename[typename].total += 1;
        testsResultByTypename.ALL.total += 1;
        switch (t.resultKind) {
          case resultKind.unknown:
            testsResultByTypename[typename].unexcuted += 1; // 未执行
            testsResultByTypename.ALL.unexcuted += 1; // 未执行
            t.resultString = 'unexcuted';
            break;
          case resultKind.succeed:
          case resultKind.nonStrictSucceed:
          case resultKind.noResult:
            testsResultByTypename[typename].passed += 1;
            testsResultByTypename[typename].excuted += 1;
            testsResultByTypename.ALL.passed += 1;
            testsResultByTypename.ALL.excuted += 1;
            t.resultString = 'passed';
            break;
          default:
            testsResultByTypename[typename].notPassed += 1;
            testsResultByTypename[typename].excuted += 1;
            testsResultByTypename.ALL.notPassed += 1;
            testsResultByTypename.ALL.excuted += 1;
            t.resultString = 'notPassed';
            break;
        }
      });
    });
    const funcMap = arrayToMap(allFuncs, '_id');
    allTests.forEach((t) => {
      const func = funcMap.get(t.functionId.toString());
      t.func = func;
      func.tests = func.tests || [];
      func.tests.push(t);
    });
    let noTestIdx = 0;
    allFuncs.forEach((func, idx) => {
      func.tests = func.tests || [];
      // 按照typeNumberString排序
      func.tests = func.tests.sort((t1, t2) => (t1.typeNumberString > t2.typeNumberString ? 1 : -1));
      func.idx = idx + 1;
      if (func.tests.length > 0) {
        noTestIdx += 1;
        func.noTestIdx = noTestIdx;
      }
    });
    summary.allFuncs = allFuncs;
    summary.allFuncs.forEach((func) => {
      func.codeScale = '-';
      const { location, endLocation } = func;
      if (location && endLocation && location.line && endLocation.line) {
        func.codeScale = endLocation.line - location.line + 1;
      }
      const testTypeMap = func.tests.reduce((map, t) => {
        const { typeName, typeNumber } = t;
        let { typeNumberString } = t;
        if (typeName && typeNumberString) {
          let funcName;
          if (func.parents && func.parents.length > 0) {
            funcName = func.parents.reduce((pre, cur) => {
              if (classMap[cur._id.toString()] !== undefined) {
                pre.push(classMap[cur._id.toString()]);
              }
              return pre;
            }, []);
          }
          funcName.push(func.functionName);
          funcName = funcName.join('_');
          typeNumberString = `${typeName}_${funcName}_${`${typeNumber}`.padStart(3, '0')}`;
          const val = map.get(typeName);
          const delimiter = '~';
          if (val) {
            const [begin] = val.split(delimiter);
            const typeNumberStr = `${typeNumber}`.padStart(3, '0');
            map.set(typeName, `${begin}${delimiter}${typeNumberStr}`);
          } else {
            map.set(typeName, typeNumberString);
          }
        }
        return map;
      }, new Map());
      func.testRangeByType = [...testTypeMap.values()].map((str, idx, arr) => ({
        str,
        isLast: idx === arr.length - 1,
      }));
      func.typeNameStr = [...testTypeMap.keys()].map((typename, idx, arr) => ({
        str: `${typeNameDic[typename]}测试`,
        isLast: idx === arr.length - 1,
      }));
    });
    summary.allTests = allFuncs.reduce((list, func) => [...list, ...func.tests], []);
    summary.allTests.forEach((t, idx) => {
      t.idx = idx + 1;
    });
    summary.uncoveredFunctionsOverview = summary.functionsOverview.filter((item) => {
      const { coverageInfo } = item;
      // eslint-disable-next-line no-restricted-syntax
      for (const info of coverageInfo) {
        const { total, covered } = info;
        if (total > 0 && covered < total) {
          return true;
        }
      }
      return false;
    });
    summary.testComposition = summary.functionsOverview.filter((item) => item.integrationFunctions && item.integrationFunctions.length > 0);
    summary.testComposition.forEach((item, idx) => {
      item.idx = idx + 1;
      item.calls = item.integrationFunctions.map(({ name }) => name).join('、');
    });
  }

  async generateSummaryReport(data, output, baseDir = this.baseDir) {
    await super.generateSummaryReport(data, output, baseDir);
    const { projectName } = data.overview;
    await this.generateReport('summary-test-result', data, path.join(path.dirname(output), `${projectName}项目测试结果`), { isSummary: true, baseDir: __dirname });
    const allTestsSet = arrayToMap(data.allTests, '_id');
    await this.generateReport('summary-tests-set', {
      ...data,
      // 从handlefile中收集的测试用例集数据
      summaryTestsSet: this.summaryTestsSet.map((item, idx) => {
        const test = allTestsSet.get(item.testId.toString());
        item.index = `${idx + 1}`.padStart(3, 0);
        if (test) {
          item.functionName = test.func.functionName;
          item.projectName = projectName;
        }
        return item;
      }),
    }, path.join(path.dirname(output), `${projectName}项目测试用例集`), { isSummary: true, baseDir: __dirname });
    this.summaryTestsSet = [];
  }

  /**
   * 获取用例列表详细信息
   * @param {Object} typeSystem 类型系统
   * @param {Object} func 函数信息
   */
  getTestDetailsOfFunction(typeSystem, func) {
    const resTests = [];
    func.tests.sort((a, b) => {
      const c1Time = a.group ? mongoose.Types.ObjectId(a.weight.substr(0, a.weight.length - 4)).getTimestamp().getTime()
        : a._id.getTimestamp().getTime();
      const c2Time = b.group ? mongoose.Types.ObjectId(b.weight.substr(0, b.weight.length - 4)).getTimestamp().getTime()
        : b._id.getTimestamp().getTime();
      if (typeof a.group === typeof b.group) {
        return a.weight > b.weight ? 1 : -1;
      }
      return c1Time > c2Time ? 1 : -1;
    });
    const funcVariables = functionsModel.getFunctionVariableJson(func.variables);
    if (!this.isIntegrationTest()) {
      typeSystem = files.getTypesJson(func.fileId, typeSystem);
    }
    func.tests.forEach((test) => {
      const testCase = tests.getJson(test, this.i18n);
      const ReportTestCaseTransformer = classCreator(this.isIntegrationTest() && 'integrationTest');
      const transformer = new ReportTestCaseTransformer(
        testCase,
        funcVariables,
        typeSystem,
        this.i18n
      );
      try {
        transformer.transform(this.complete);
      } catch (error) {
        logger.error(`testCase transform error: fileId: ${func.fileId} function name: ${func.functionName} test case number: ${test.number}:`, error);
        return;
      }

      const { coverages } = this.version;
      let statementCoverage; let branchCoverage; let conditionCoverage; let mcdcCoverage;
      let invokeCoverage; let invokedFuncCoverage; let decisionCoverage;
      if ((coverages & tests.coverageKind.statement) > 0) {
        statementCoverage = '-';
        if (func.statementTotal && !this.isValidResultKind(test.resultKind)) {
          statementCoverage = '0%';
        } else if (func.statementTotal) {
          statementCoverage = test.statementCovered / func.statementTotal;
          statementCoverage = `${Math.round(statementCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.branch) > 0) {
        branchCoverage = '-';
        if (func.branchTotal && !this.isValidResultKind(test.resultKind)) {
          branchCoverage = '0%';
        } else if (func.branchTotal) {
          branchCoverage = test.branchCovered / func.branchTotal;
          branchCoverage = `${Math.round(branchCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.condition) > 0) {
        conditionCoverage = '-';
        if (func.conditionTotal && !this.isValidResultKind(test.resultKind)) {
          conditionCoverage = '0%';
        } else if (func.conditionTotal) {
          conditionCoverage = test.conditionCovered / func.conditionTotal;
          conditionCoverage = `${Math.round(conditionCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.decision) > 0) {
        decisionCoverage = '-';
        if (func.decisionTotal && !this.isValidResultKind(test.resultKind)) {
          decisionCoverage = '0%';
        } else if (func.decisionTotal) {
          decisionCoverage = test.decisionCovered / func.decisionTotal;
          decisionCoverage = `${Math.round(decisionCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.mcdc) > 0) {
        mcdcCoverage = '-';
        if (func.mcdcTotal && !this.isValidResultKind(test.resultKind)) {
          mcdcCoverage = '0%';
        } else if (func.mcdcTotal) {
          mcdcCoverage = test.mcdcCovered / func.mcdcTotal;
          mcdcCoverage = `${Math.round(mcdcCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.invoke) > 0) {
        invokeCoverage = '-';
        if (func.invokeTotal && !this.isValidResultKind(test.resultKind)) {
          invokeCoverage = '0%';
        } else if (func.invokeTotal) {
          invokeCoverage = test.invokeCovered / func.invokeTotal;
          invokeCoverage = `${Math.round(invokeCoverage * 10000) / 100}%`;
        }
      }
      if ((coverages & tests.coverageKind.invokedFunc) > 0) {
        invokedFuncCoverage = '-';
        if (func.invokedFuncTotal && !this.isValidResultKind(test.resultKind)) {
          invokedFuncCoverage = '0%';
        } else if (func.invokedFuncTotal) {
          invokedFuncCoverage = test.invokedFuncCovered / func.invokedFuncTotal;
          invokedFuncCoverage = `${Math.round(invokedFuncCoverage * 10000) / 100}%`;
        }
      }
      let annotations;
      test.udfs.forEach((udf) => {
        if (udf.name === '用例注释') {
          annotations = udf.value;
        }
      });
      resTests.push({
        testId: testCase.testId,
        index: test.number,
        description: testCase.description,
        identifier: testCase.identifier,
        requirementInfo: testCase.requirementInfo,
        typeName: testCase.typeName,
        typeNumber: testCase.typeNumber,
        resultKind: test.resultKindName,
        udfs: test.udfs,
        detail: {
          prelude: transformer.prelude,
          input: transformer.input,
          output: transformer.output,
          result: transformer.result,
        },
        isIntegrationTest: this.isIntegrationTest(),
        group: test.group ? test.group.map((num) => `${this.i18n.__('tests.group')}${num} / `).join('') : '',
        statementCoverage,
        branchCoverage,
        conditionCoverage,
        decisionCoverage,
        mcdcCoverage,
        invokeCoverage,
        invokedFuncCoverage,
        annotations,
        execCount: testCase.execCount,
      });
    });
    return resTests;
  }
}

/**
 * Export model definition object.
 */
module.exports = DefaultReporter;
